<html>
  <head>
    <title>EV Charge Sites DB Import</title>
    
    <script type="text/javascript" src="/static/js/third_party/json2.js"></script>
    <script type="text/javascript" src="/static/js/common.js"></script>
    <script type="text/javascript" src="/static/js/client.js"></script>
    <script type="text/javascript">

    function init(){
      renderSiteList();
    }
    
    function showProgress(show){
      showId('loading', show);
      showId('listSites', !show); 
    }
    
    function renderSiteList(){
      showProgress(true);
      server.GetSites(renderCallback, function(){
        alert('ERROR: GetSites');
      });
    }
    
    function renderCallback(rs){
      
      // TODO(pv): Pageinate these results?

      var i;
      var cols = rs.columns;
      var rows = rs.rows;
      
      if (rows.length==0){
        $('listSites').innerHTML = '<center><h2>No data.  You will need to import some.</h2></center>';
        showProgress(false);
        return;  
      }
      
      var table = <table id='tableSites' width='100%' border='1' cellpadding='0' cellspacing='0'><tr/></table>;
      for (i=0; i<cols.length; i++){
        table.tr.td += <td align='center'>{cols[i]}</td>;
      }
      table.tr.td += <td align='center'>Commands</td>;

      var tr;
      for (i=0; i<rows.length; i++){
        var site = rows[i];
        var key;       
        for(var j=0; j<cols.length; j++){
          var val = site[j]
          if (j==0){
            key = val;          
            tr = <tr id={'row'+key}/>;
            tr.td += <td align='right' nowrap='true' width='1%'>
                <input type='checkbox' id={'check' + key}/>{key}                
                </td>;
          } else {
            tr.td += <td>{val}</td>;
          }
        }
        tr.td += <td align='center' nowrap='true' width='1%'>
            <input type='button' value='Delete' id={'btnDelete' + key} onclick={'confirmDeleteSite("' + key + '")'} style='width:100%'/>
          </td>;
        table.tr += tr;
      }
      
      // Last row: Add New...
      tr = <tr id='rowNew'>
          <td align='center'><input type='button' value={'* (' + rows.length + ')'} onclick='selectAllSites()' style='width:100%'/></td>
        </tr>;
      tr.td += <td colspan={cols.length-1}><hr width='100%'/></td>;
      tr.td += <td align='center' nowrap='true'>
            <input id='btnDeleteSelected' type='button' value='Delete Selected' onclick='deleteSelected()'/>
          </td>;
      table.tr += tr;
      
      $('listSites').innerHTML = table.toString();
      showProgress(false);
    }

    function domToE4X(element){
      return new XML((new XMLSerializer()).serializeToString(element));
    }

    function getRowCheckboxInput(row){
      return row.cells[0].firstChild.nextSibling;
    }

    function getRowId(row){
      var input = getRowCheckboxInput(row);
      return input.id.substr(5);
    }
    
    function selectAllSites(){
      var table = $('tableSites');
      var rows = table.rows;
      for(var i=1; i<rows.length-1; i++){
        var id = getRowId(rows[i]);
        $('check' + id).checked = true;
      }
    }

    function getInputProperties(rowElement){
      var props;
      var tr = domToE4X(rowElement);
      if (tr){
        props = {};
        var inputs = tr..TD..TEXTAREA;
        for(var i in inputs){
          var input = inputs[i];
          var inputId = input.@id.toString();
          var key = inputId.substring(4,inputId.length-1);
          var value = $(inputId).value;
          props[key]=value;
        }
      }
      return props;
    }

    function deleteSite(id, refresh){
      $('btnDelete' + id).disabled = true;
      server.DeleteSite(id, function(){
        // onSuccess
      }, function(){
        alert('ERROR: DeleteSite');
      });
      if (refresh != false) 
        renderSiteList();
    }

    function confirmDeleteSite(id){
      if (confirm("Are you sure you want to delete site #" + id + "?")) {
        deleteSite(id);
        renderSiteList();
      }
    }
        
    function deleteSelected(){
      $('btnDeleteSelected').disabled = true;
      var numDeleted = 0;
      var table = $('tableSites');
      var rows = table.rows;
      for(var i=1; i<rows.length-1; i++){
        var id = getRowId(rows[i]);
        if ($('check' + id).checked){
          numDeleted++;
          deleteSite(id, false);
        }
      }
      if (numDeleted > 0)
        renderSiteList();
      else
        $('btnDeleteSelected').disabled = false;
    }    


    //
    // Code for importing data...
    //

    function importData(btnId, url, callbackForEach){
      
      if (confirm('Fetch the following URL?\n\n' + url)) {
      
        var btn = $(btnId);
        btn.disabled = true;
      
        // Proxy the cross-site download request to the server
        server.GetUrl(url, function(response){
        
          if (response.status_code == 200){
          
            var content = response.content;
            var r;
            
            // Remove any xml header at beginning (breaks E4X)...
            r = /^<\?xml.*?\?>.*?\n/i;
            content = content.replace(r, '');
            // Remove any xmlns attribute inside the root node (breaks E4X)... 
            r = /^<(.*?) (xmlns=(?:".*?"|'.*?'))(.*?)>/i;            
            content = content.replace(r, '<$1$3>');
            
            var xml = new XML(content);
            
 	          // TODO(pv): Would a progress dialog here be at all responsive?
            var txt = 'Fetch was successful.\n\n' +
            'Import will begin after you press OK.\n\n' +
            'The import *WILL* result in duplicates of any previously imported data!\n\n' +
            'NOTE: THIS MAY TAKE SEVERAL MINUTES TO COMPLETE!\n\n' +
            'Are you sure you want to proceed w/ the import?';

 	        if (confirm(txt)) {
 	          
              showProgress(true);

              callbackForEach(xml, function(props){
                var name = props.name;
                var latlng = props.latlng;
                delete props.name;
                delete props.latlng;
                server.AddSite(name, latlng, props, function(){
                  // onSuccess
                }, function(){
                  // onError
                  alert('ERROR: AddSite "'+name+'"');
                });
              }, function(){
                btn.disabled = false;
                renderSiteList();              
              });

            } else {

              btn.disabled = false;
            }
          }
        }, function(){
          btn.disabled = false;
          alert('ERROR: GetURL');
        });
      }
    }

    function xmlGetValue(root, tagname){
      var node = root.getElementsByTagName(tagname);
      if (node.length > 0) {
        if (node[0].firstChild != null) {
          return node[0].firstChild.nodeValue;
        }
      };
      return "";
    }
    
    function importEVChargerMaps(){
      importData('btnImportEVChargerMaps',
          'http://www.evchargermaps.com/XMLs/sitesxml.xml',
          function(xml, callbackForEach, callbackComplete){

            var TechList = {
              "avc": "avcon",
              "spi": "spi",
              "lpi": "lpi",
              "othercond": "condOther"
            };
            
            var regions = [];
            
            for each (var site in xml.site){

              var services = [];
              
              var techs = {};
              for (var key in TechList) {
                var techName = TechList[key];
                
                var techNode = site[key];
                var techInfo = {
                  number: parseInt(techNode.@number.toString() || 0),
                  up: parseInt(techNode.@up.toString() || 0),
                  down: parseInt(techNode.@down.toString() || 0),
                  marginal: parseInt(techNode.@marginal.toString() || 0)
                };
                techInfo.unknown = Math.max(0, techInfo.number - (techInfo.up + techInfo.down + techInfo.marginal));
                
                for(var i=0; i<techInfo.up; i++){
                  var service = {type:techName, condition:'Working'};
                  services.push(service);
                }
                for(var i=0; i<techInfo.down; i++){
                  var service = {type:techName, condition:'Not Working'};
                  services.push(service);
                }
                for(var i=0; i<techInfo.marginal; i++){
                  var service = {type:techName, condition:'Marginal'};
                  services.push(service);
                }
                for(var i=0; i<techInfo.unknown; i++){
                  var service = {type:techName, condition:'Unknown'};
                  services.push(service);
                }
              }
              
              var name = site.name.toString();
              name = name.replace('\n','');
              
              // Weird structure for text:
              // region, city[, state]
              var cityStateCountry;
              var regionCityState = site.city.toString();
              var matches = regionCityState.match(/^(.*?),(.*?)(?:,(.*?))?$/);
              if (matches){
              
                var region = matches[1].trim();
                var city = matches[2].trim();
                var state = matches[3].trim(); // TODO(pv): Will index 3 always exist?
                var country = 'USA';
                
                switch (region.toLowerCase()){
                
                  case 'aos':{
	                switch (state.toUpperCase()){
	                  case '':
	                    state = 'CA';
	                    break;
	                  case 'FR':
	                    state = null;
	                    country = 'France';
	                    break;
	                }
                    break;
                  }
                  
                  case 'ga':{
                    state = 'GA';
                    break;
                  }
                  
                  // TODO(pv): Add more region handlers here as needed
                  
                  default:{
                    // region: bay, la, oc, sac, sba, sbo, sd, ven, ...
                     
                    // If the state is missing, default to 'CA'
                    state = 'CA';
                    
                    // Remove some stray descriptions from city names
                    city = city.replace(' - Airport','');
                    city = city.replace(' - Downtown','');
                    city = city.replace(' - other','');
                  }                                      
                }
                
                if (state){
                  cityStateCountry = city+', '+state+', '+country;
                } else {
                  cityStateCountry = city+', '+country;
                } 
                
                if (regions.indexOf(region) == -1)
                  regions.push(region);
                
              } else {
                alert('ERROR: site("'+name+'").city did not match "region, city[, state]"');
                break;
              }
              
              var id = site.id.toString();
              var urlDetails = 'http://www.evchargernews.com/regions/'+id+'.htm';
              var urlComments = 'http://evchargermaps.com/data/'+id;
              
              var props = {
                name: name,
                latlng: '(' + site.latlng.toString() + ')',
                address: site.address.toString() + " \n" + cityStateCountry,
                description: 'action="'+site.action.toString().trim()+'"\n'+
                    'Imported from <a href="http://www.evchargermaps.com">EVChargerMaps.com</a> (id=<a href="'+urlDetails+'">'+id+'</a>',
                pay: site.pay.toString(),
                restricted: site.restricted.toString(),
                services: services,
                evcmid:id
              };

              callbackForEach(props);

            }
            callbackComplete();
          });
    }
        
    function importNWChargerSites(){
      importData('btnImportNWChargeSites',
          'http://maps.google.com/maps/ms?ie=UTF8&hl=en&oe=UTF8&msa=0&msid=106101835885297831085.00044b128b91711066112&output=kml',
          function(xml, callbackForEach, callbackComplete){
            for each (var placemark in xml.Document.Placemark) {
            
              var coords = placemark.Point.coordinates.toString().split(',');

              var name = placemark.name.toString();
              var latlng = '('+coords[1] + ',' + coords[0]+')';
              var description = placemark.description.toString();

              // why are there non utf8 chars in the KML's "more>>" text?
              description = description.replace(/.»/g, '...');
                            
              //alert('before:'+description);
              description = description.replace(/<\/?span[^>]*?>/gi, ''); // remove all SPANs
              description = description.replace(/\n/g, ''); // remove all \n
              description = description.replace(/&nbsp;/gi, ' '); // replace '&nbsp;' w/ ' '
              description = description.replace(/<br>/gi, '\n'); // replace BR w/ \n
              description = description.replace(/\n(\s*\n)+/g, '\n'); // remove duplicate \n
              description = description.replace(/<(div)[^>]*?>/gi, '<$1>'); // remove attributes from DIV
              description = description.replace(/<(\w+)[^>]*?>\s*<\/\1>/gi, ''); // Remove empty tags
              description = description.replace(/<\/(\w+[^>]*?)>\s*</gi, '</$1><'); // remove all \s *between* tags                            
              description = description.replace(/(<\w+[^>]*?>)\s*/gi, '$1'); // remove trailing \s in value after >
              description = description.replace(/\s*(<\/\w+[^>]*?>)/gi, '$1'); // remove leading \s in value before </
              description = description.replace(/(<\/\w+[^>]*?>)(?!\n)/gi, '$1\n'); // suffix </*> w/ \n
              description = description.replace(/^\s*/, '').replace(/\s*$/, ''); // Strip leading/trailing white spaces
              description = description.replace(/\n(?!<\/?div)/gi, '<br>\n'); // Replace \n w/ BR\n
              description = description.replace(/<(\/?)(\w+)([^>]*?)>/gm, function(m, m1, m2, m3){
                return '<' + m1 + m2.toUpperCase() + m3 + '>'; // UpperCase all HTML tags
              });              
              if (description.indexOf('<DIV>') != 0){
                description = '<DIV>'+description+'</DIV>';
              }
              //alert('after:"'+description+'"');
              
              var address = "Unknown: Please manually move from description (if applicable)";

              // Strip out things that confuse phone # search...
              var temp = description;
              temp = temp.replace(/<\/?[^>]*?>/gi, '');
              
              // Mostly from: http://javascript.about.com/library/blre.htm
              var rePhone = /((\+\d{1,3}[- \.]?\(?\d\)?[- \.]?\d{1,5})|(\(?\d{2,6}\)?))[- \.]?(\d{3,4})[- \.]?(\d{4})(( ?x| ?ext)\d{1,5}){0,1}/gi;
              var contactPhone = temp.match(rePhone);
              if (contactPhone){
                var i = 0;
                if (contactPhone.length > 1){
                  var txt = 'Found more than one phone # for site.';
                  for(i=0; i<contactPhone.length; i++){
                    txt += '\n'+i+') "'+contactPhone[i]+'"';
                  }
                  txt += '\nWhich one do you want to use?';
                  var choice = prompt(txt, 0);
                  i = (choice) ? parseInt(choice) : 0;
                }
                contactPhone = contactPhone[i];
                //alert('Found phone # "'+contactPhone+'"');
                
                description = description.replace(contactPhone, '');

                // TODO(pv): Fine tune the phone #? (for a consistent look)
                //temp = contactPhone.replace(/[^\d]*(?!(...
                //var part = contactPhone.match(/\d*...

              } else {
                contactPhone = '???-???-????';
                //alert('Could not find phone # in:\n"'+temp+'"');
              }
              
              var props = {
                name: name,
                latlng: latlng,
                address: address,
                contactPhone: contactPhone,
                description: description
              };

              callbackForEach(props);
            }
            callbackComplete();
          });
    }

    function wait(msecs){
      var start = new Date().getTime();
      var cur = start
      while(cur - start < msecs){
        cur = new Date().getTime();
      }
    } 

    function importEVChargerMapsComments(){
      var table = $('tableSites');
      if (!table)
        return;
      
      if (!confirm('Scrapping comments will *INTENTIONALLY* take a very long time.\n'+
                  'Are you sure you want to do this?'))
        return;
                 
      var rows = table.rows;
      for(var i=1; i<rows.length-1; i++){

        var id = getRowId(rows[i]);
        if (!$('check' + id).checked)
          continue;

        //wait(2000);
                  
        server.GetSite(id, function(details){
          var urlComments = 'http://evchargermaps.com/data/'+details.evcmid;
          server.GetUrl(urlComments, function(response){
            if (response.status_code == 200){
              alert(response.content);
              //server.UpdateSite(id, {comment:response.content});
            }            
          });
        });

      }
    }
         
    </script>
  </head>
  <body onload='init()'>
    <h1 align='center'><a href='/'>EV Charge Sites</a> DB Import</h1>
    <table width='100%' border='0'>
      <tr>
        <td align='center' colspan='2'><input type='button' style='width:100%' value='Refresh' onclick='renderSiteList()'/></td>
      </tr>
      <tr>
        <td align='center' colspan='2'>
          <div id='loading'><center><img src="/static/img/loading.gif"/></center></div>
         <div id='listSites' style='display:none'></div>
        </td>        
      </tr>
      <tr>
        <td align='center' colspan='2'><input type='button' style='width:100%' value='Refresh' onclick='renderSiteList()'/></td>
      </tr>
      <tr>
        <td align='center'>
          <input id='btnImportEVChargerMaps' type='button' style='width:100%' value='Import EVChargerMaps' onclick='importEVChargerMaps()'/>
        </td>
         <td align='center'>
           <input id='btnImportNWChargeSites' type='button' style='width:100%' value='Import NW Charger Sites' onclick='importNWChargerSites()'/>
         </td>
      </tr>
      <tr>
        <td align='center' colspan='2'>
          <input id='btnImportComments' type='button' style='width:100%' value='Import EVChargerMaps Comments' onclick='importEVChargerMapsComments()'/>
        </td>
      </tr>
    </table>
  </body>
</html>
